package ss.pku.jsa.type.inference;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;

@SuppressWarnings({"unchecked","rawtypes"})
public class MethodKey extends JqualKey implements Serializable, Comparable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private static Map<Key, MethodKey> cache = new HashMap();
	private String key;
	private TypeKey declaringType;

	public static MethodKey getMethodKey(IMethodBinding m) {
		String k = m.getKey();
		ITypeBinding type = m.getDeclaringClass();

		Profile.logMemStart("MethodKey(IMethod)/new TypeKey");
		TypeKey t = TypeKey.getTypeKey(type);
		Profile.logMemEnd();

		return sharedGetMethodKey(k, t);
	}

	public static MethodKey getMethodKey(IMethod m) {
		String k = m.getKey();
		IType type = m.getDeclaringType();

		Profile.logMemStart("MethodKey(IMethod)/new TypeKey");
		TypeKey t = TypeKey.getTypeKey(type.getKey(), 0,
				type.getFullyQualifiedName());
		Profile.logMemEnd();

		return sharedGetMethodKey(k, t);
	}

	private static MethodKey sharedGetMethodKey(String k, TypeKey t) {
		Key mkey = new Key(t, k);
		MethodKey ret = (MethodKey) cache.get(mkey);
		if (ret == null) {
			ret = new MethodKey(t, k);
			cache.put(mkey, ret);
		}
		return ret;
	}

	public static MethodKey getInitMethodKey(ITypeBinding t) {
		Profile.logMemStart("MethodKey(ITypeBinding)/new TypeKey");
		TypeKey tk = TypeKey.getTypeKey(t);
		Profile.logMemEnd();

		String k = tk.toString() + "_init";
		return sharedGetMethodKey(k, tk);
	}

	private MethodKey(TypeKey t, String k) {
		this.declaringType = t;
		this.key = k;
	}

	public String toString() {
		return this.key;
	}

	public TypeKey getDeclaringType() {
		return this.declaringType;
	}

	public int compareTo(Object o) {
		return toString().compareTo(o.toString());
	}

	private static class Key {
		private String key;
		private TypeKey declaringType;

		public Key(TypeKey declaringType, String key) {
			this.key = key;
			this.declaringType = declaringType;
		}

		public int hashCode() {
			int result = 1;
			result = 31
					* result
					+ (this.declaringType == null ? 0 : this.declaringType
							.hashCode());
			result = 31 * result + (this.key == null ? 0 : this.key.hashCode());
			return result;
		}

		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Key other = (Key) obj;
			if (this.declaringType == null) {
				if (other.declaringType != null)
					return false;
			} else if (!this.declaringType.equals(other.declaringType))
				return false;
			if (this.key == null) {
				if (other.key != null)
					return false;
			} else if (!this.key.equals(other.key))
				return false;
			return true;
		}
	}
}
